#include "stdAfx.h"
#include "Camera.h"
#include "objectmanager.h"

CCamera::CCamera() 
{ 
	m_d3dxvRight = D3DXVECTOR3(1.0f, 0.0f, 0.0f); 
	m_d3dxvUp = D3DXVECTOR3(0.0f, 1.0f, 0.0f); 
	m_d3dxvLook = D3DXVECTOR3(0.0f, 0.0f, 1.0f); 
	
	m_d3dxvPivot = D3DXVECTOR3(0.0f,0.0f,0.0f);
	m_d3dxvOffset = D3DXVECTOR3(0.0f,0.0f,0.0f);
	m_d3dxvPos = D3DXVECTOR3(0.0f, 0.0f, 0.0f); 

	m_fNearPlaneDistance = 1.01f;
	m_fFarPlaneDistance = 5000.0f;
	m_fFOVAngle = 60.0f;

	D3DXMatrixIdentity(&m_d3dxmtxView);
	D3DXMatrixIdentity(&m_d3dxmtxProjection);

}

CCamera::~CCamera() 
{ 
}
void CCamera::MoveForward(float elapsedTime)
{
	m_d3dxvPivot += (m_d3dxvLook * elapsedTime);
	m_d3dxvPos += (m_d3dxvLook * elapsedTime);
}
void CCamera::MoveBackward(float elapsedTime)
{
	m_d3dxvPivot -= (m_d3dxvLook * elapsedTime);
	m_d3dxvPos -= (m_d3dxvLook * elapsedTime);
}

void CCamera::MoveRight(float elapsedTime)
{
	m_d3dxvPivot += (m_d3dxvRight * elapsedTime);
	m_d3dxvPos += (m_d3dxvRight * elapsedTime);
}
void CCamera::MoveLeft(float elapsedTime)
{
	m_d3dxvPivot -= (m_d3dxvRight * elapsedTime);
	m_d3dxvPos -= (m_d3dxvRight * elapsedTime);
}

void CCamera::SetLookAtPoint(const D3DXVECTOR3& d3dxvLookAtPoint)
{
	D3DXVec3Normalize(&m_d3dxvLook ,&D3DXVECTOR3(d3dxvLookAtPoint - m_d3dxvPos));
	D3DXVECTOR3 up(0.0f, 1.0f, 0.0);

	D3DXVec3Cross( &m_d3dxvRight, &up, &m_d3dxvLook);
	D3DXVec3Cross( &m_d3dxvUp, &m_d3dxvLook, &m_d3dxvRight);
}

void CCamera::UpdateViewMatrix(LPDIRECT3DDEVICE9 pD3DDevice) 
{
    D3DXVec3Normalize(&m_d3dxvLook, &m_d3dxvLook);
    D3DXVec3Cross(&m_d3dxvRight, &m_d3dxvUp, &m_d3dxvLook);
    D3DXVec3Normalize(&m_d3dxvRight, &m_d3dxvRight);
    D3DXVec3Cross(&m_d3dxvUp, &m_d3dxvLook, &m_d3dxvRight);
    D3DXVec3Normalize(&m_d3dxvUp, &m_d3dxvUp);

    m_d3dxmtxView._11 = m_d3dxvRight.x; m_d3dxmtxView._12 = m_d3dxvUp.x; m_d3dxmtxView._13 = m_d3dxvLook.x;
    m_d3dxmtxView._21 = m_d3dxvRight.y; m_d3dxmtxView._22 = m_d3dxvUp.y; m_d3dxmtxView._23 = m_d3dxvLook.y;
    m_d3dxmtxView._31 = m_d3dxvRight.z; m_d3dxmtxView._32 = m_d3dxvUp.z; m_d3dxmtxView._33 = m_d3dxvLook.z;
    m_d3dxmtxView._41 =- D3DXVec3Dot(&m_d3dxvPos, &m_d3dxvRight);
    m_d3dxmtxView._42 =- D3DXVec3Dot(&m_d3dxvPos, &m_d3dxvUp);
    m_d3dxmtxView._43 =- D3DXVec3Dot(&m_d3dxvPos, &m_d3dxvLook);
	pD3DDevice->SetTransform(D3DTS_VIEW, &m_d3dxmtxView);
}

void CCamera::UpdateProjectionMatrix(LPDIRECT3DDEVICE9 pD3DDevice) 
{
	float fAspectRatio = (float)m_d3dViewport.Width / (float)m_d3dViewport.Height;
	D3DXMatrixPerspectiveFovLH(&m_d3dxmtxProjection, D3DXToRadian(m_fFOVAngle), fAspectRatio, m_fNearPlaneDistance, m_fFarPlaneDistance);
	pD3DDevice->SetTransform(D3DTS_PROJECTION, &m_d3dxmtxProjection);
}

void CCamera::SetViewport(DWORD xStart, DWORD yStart, DWORD nWidth, DWORD nHeight, float fMinZ, float fMaxZ)
{
	m_d3dViewport.X = xStart;
	m_d3dViewport.Y = yStart;
	m_d3dViewport.Width = nWidth;
	m_d3dViewport.Height = nHeight;
	m_d3dViewport.MinZ = fMinZ;
	m_d3dViewport.MaxZ = fMaxZ;
}

void CCamera::Rotate(float fPitch , float fYaw , float fRoll )
{
	D3DXMATRIX mtxRotate;
    if (fPitch != 0.0f) 
    {
        D3DXMatrixRotationAxis(&mtxRotate, &m_d3dxvRight, D3DXToRadian(fPitch));        
        D3DXVec3TransformNormal(&m_d3dxvLook, &m_d3dxvLook, &mtxRotate);
        D3DXVec3TransformNormal(&m_d3dxvUp, &m_d3dxvUp, &mtxRotate);
        D3DXVec3TransformNormal(&m_d3dxvRight, &m_d3dxvRight, &mtxRotate);
    } 
    if (fYaw != 0.0f)
    {
        D3DXMatrixRotationAxis(&mtxRotate, &m_d3dxvUp, D3DXToRadian(fYaw));
        D3DXVec3TransformNormal(&m_d3dxvLook, &m_d3dxvLook, &mtxRotate);
        D3DXVec3TransformNormal(&m_d3dxvUp, &m_d3dxvUp, &mtxRotate);
        D3DXVec3TransformNormal(&m_d3dxvRight, &m_d3dxvRight, &mtxRotate);    } 
    if ( fRoll != 0.0f)
    {
        D3DXMatrixRotationAxis(&mtxRotate, &m_d3dxvLook, D3DXToRadian(fRoll));      
        D3DXVec3TransformNormal(&m_d3dxvLook, &m_d3dxvLook, &mtxRotate);
        D3DXVec3TransformNormal(&m_d3dxvUp, &m_d3dxvUp, &mtxRotate);
        D3DXVec3TransformNormal(&m_d3dxvRight, &m_d3dxvRight, &mtxRotate);
    } 
}

void CCamera::RotateOffset( float fPitch , float fYaw , float fRoll )
{
	Rotate(fPitch,fYaw,fRoll);

	D3DXVECTOR3 d3dxvOffset = m_d3dxvOffset;
	
	D3DXMATRIX mtxRotate;
	D3DXMatrixIdentity(&mtxRotate);

	mtxRotate._11 = m_d3dxvRight.x; mtxRotate._21 = m_d3dxvUp.x; mtxRotate._31 = m_d3dxvLook.x;
	mtxRotate._12 = m_d3dxvRight.y; mtxRotate._22 = m_d3dxvUp.y; mtxRotate._32 = m_d3dxvLook.y;
	mtxRotate._13 = m_d3dxvRight.z; mtxRotate._23 = m_d3dxvUp.z; mtxRotate._33 = m_d3dxvLook.z;

	D3DXVec3TransformCoord(&d3dxvOffset,&d3dxvOffset,&mtxRotate);

	m_d3dxvPos= m_d3dxvPivot + d3dxvOffset;
}
bool CCamera::IsInFrustum(const D3DXVECTOR3& vPos,const float fRadius)
{
	D3DXVECTOR3	vNormal;
	for(int i = 0 ; i < 6; i ++)
	{
		vNormal = D3DXVECTOR3(m_FrustumPlane[i].a, m_FrustumPlane[i].b, m_FrustumPlane[i].c);
		float distance = D3DXVec3Dot(&vNormal,&vPos) + m_FrustumPlane[i].d ;
		if( D3DXVec3Dot(&vNormal,&vPos) + m_FrustumPlane[i].d  > fRadius )
			return false;
	}
	return true;
}
bool CCamera::IsInFrustum(const D3DXVECTOR3& vMinimum,const D3DXVECTOR3& vMaximum)
{
	D3DXVECTOR3 vNearPoint, vFarPoint, vNormal;

    for (int i = 0; i < 6; i++)
    {
        vNormal = D3DXVECTOR3(m_FrustumPlane[i].a, m_FrustumPlane[i].b, m_FrustumPlane[i].c);
        if (vNormal.x > 0.0f)
        {
            if (vNormal.y > 0.0f)
            {
                if (vNormal.z > 0.0f) 
                {
                    vNearPoint.x = vMinimum.x; vNearPoint.y = vMinimum.y; vNearPoint.z = vMinimum.z;                
                } 
                else 
                {
                    vNearPoint.x = vMinimum.x; vNearPoint.y = vMinimum.y; vNearPoint.z = vMaximum.z;                 
                } 
            } 
            else
            {
                if (vNormal.z > 0.0f) 
                {
                    vNearPoint.x = vMinimum.x; vNearPoint.y = vMaximum.y; vNearPoint.z = vMinimum.z;
                } 
                else 
                {
                    vNearPoint.x = vMinimum.x; vNearPoint.y = vMaximum.y; vNearPoint.z = vMaximum.z;                 
                } 
            } 
        } 
        else
        {
            if (vNormal.y > 0.0f)
            {
                if (vNormal.z > 0.0f) 
                {
                    vNearPoint.x = vMaximum.x; vNearPoint.y = vMinimum.y; vNearPoint.z = vMinimum.z;
                } 
                else 
                {
                    vNearPoint.x = vMaximum.x; vNearPoint.y = vMinimum.y; vNearPoint.z = vMaximum.z;                 
                } 
            } 
            else
            {
                if (vNormal.z > 0.0f) 
                {
                    vNearPoint.x = vMaximum.x; vNearPoint.y = vMaximum.y; vNearPoint.z = vMinimum.z;                
                } 
                else 
                {
                    vNearPoint.x = vMaximum.x; vNearPoint.y = vMaximum.y; vNearPoint.z = vMaximum.z;                 
                } 
            } 
        } 
        if ((D3DXVec3Dot(&vNormal, &vNearPoint) + m_FrustumPlane[i].d) > 0.0f) return(false);
    } 
    return(true);
}
void CCamera::CalculateFrustumPlane()
{
	D3DXMATRIX mtx = m_d3dxmtxView *  m_d3dxmtxProjection;

	//left
	m_FrustumPlane[0].a = - ( mtx._14 + mtx._11);
	m_FrustumPlane[0].b = - ( mtx._24 + mtx._21);
	m_FrustumPlane[0].c = - ( mtx._34 + mtx._31);
	m_FrustumPlane[0].d = - ( mtx._44 + mtx._41);

	// right
	m_FrustumPlane[1].a = - ( mtx._14 - mtx._11);
	m_FrustumPlane[1].b = - ( mtx._24 - mtx._21);
	m_FrustumPlane[1].c = - ( mtx._34 - mtx._31);
	m_FrustumPlane[1].d = - ( mtx._44 - mtx._41);
	
	//top
	m_FrustumPlane[2].a = - ( mtx._14 - mtx._12);
	m_FrustumPlane[2].b = - ( mtx._24 - mtx._22);
	m_FrustumPlane[2].c = - ( mtx._34 - mtx._32);
	m_FrustumPlane[2].d = - ( mtx._44 - mtx._42);

	//bottom
	m_FrustumPlane[3].a = - ( mtx._14 + mtx._12);
	m_FrustumPlane[3].b = - ( mtx._24 + mtx._22);
	m_FrustumPlane[3].c = - ( mtx._34 + mtx._32);
	m_FrustumPlane[3].d = - ( mtx._44 + mtx._42);

	//near
	m_FrustumPlane[4].a = - ( mtx._13	);
	m_FrustumPlane[4].b = - ( mtx._23	);
	m_FrustumPlane[4].c = - ( mtx._33	);
	m_FrustumPlane[4].d = - ( mtx._43	);

	//far
	m_FrustumPlane[5].a = - ( mtx._14 - mtx._13);
	m_FrustumPlane[5].b = - ( mtx._24 - mtx._23);
	m_FrustumPlane[5].c = - ( mtx._34 - mtx._33);
	m_FrustumPlane[5].d = - ( mtx._44 - mtx._43);

	for(int i = 0; i < 6; i ++)
		D3DXPlaneNormalize(&m_FrustumPlane[i],&m_FrustumPlane[i]);
}



void CCamera::CheckRayCollision(int nScreenX, int nScreenY)
{

	D3DXVECTOR3 vCameraDir;
	vCameraDir.x =  ( (float)(2 * nScreenX) /(m_d3dViewport.Width)  -1) / m_d3dxmtxProjection._11 ;
	vCameraDir.y = -( (float)(2 * nScreenY) /(m_d3dViewport.Height) -1) / m_d3dxmtxProjection._22 ;
	vCameraDir.z =  1;

	CObjectManager::CheckRayCollision(vCameraDir, &m_d3dxmtxView);

}